/*
 *  SceneLoader.h
 *
 *  Created by Carl Schissler on 8/16/09.
 *  
 *  File contains the interface for reading and writing XML-based 3D scene
 *  description files. Use loadScene(fileName) to read a scene file and
 *  saveScene(scene,fileName) to write a scene to a file.
 *  
 *  Feel free to use this code without credit to or permission from its author.
 *
 */

#ifndef _INCLUDE_SCENE_LOADER_H_
#define _INCLUDE_SCENE_LOADER_H_


#include <vector>
#include <string>


namespace scene
{



class Transform
{
	public:
		
		/// A class which represents a single affine transformation.
		/**
		  * This transformation first scales, then translates, then rotates
		  * whatever it is applied to.
		  */
		class Node
		{
			public:
				
				inline Node()
					:	scaleX(1.0f), scaleY(1.0f), scaleZ(1.0f),
						x(0.0f), y(0.0f), z(0.0f),
						axisX(1.0f), axisY(0.0f), axisZ(0.0f),
						degrees(0.0f)
				{
				}
				
				
				/// The X, Y, and Z scale amounts.
				float scaleX, scaleY, scaleZ;
				
				/// The amount to translate.
				float x, y, z;
				
				/// The axis around which to rotate.
				float axisX, axisY, axisZ;
				
				/// The number of degrees to rotate.
				float degrees;
		};
		
		
		inline Transform()
		{
		}
		
		
		inline Transform( const std::vector<Node>& newNodes )
			:	nodes( newNodes )
		{
		}
		
		
		std::vector<Node> nodes;
		
};



//##########################################################################################
//##########################################################################################
//############		
//############		Color Class
//############		
//##########################################################################################
//##########################################################################################




/// A class representing a 4-channel RGBA color. Channel values range from 0.0f to 1.0f.
class Color
{
	public:
		
		/// Create a default color (opaque black).
		inline Color()
			:	r( 0.0f ),
				g( 0.0f ),
				b( 0.0f ),
				a( 1.0f )
		{
		}
		
		
		/// Create a fully opaque color.
		inline Color( float newR, float newG, float newB )
			:	r( newR ),
				g( newG ),
				b( newB ),
				a( 1.0f )
		{
		}
		
		
		/// Create a color from red, green, blue, and alpha channels.
		inline Color( float newR, float newG, float newB, float newA )
			:	r( newR ),
				g( newG ),
				b( newB ),
				a( newA )
		{
		}
		
		
		/// Color components. 1.0f == channel at full, 0.0f == channel at 0%.
		float r, g, b, a;
		
		
};




//##########################################################################################
//##########################################################################################
//############		
//############		Material Class
//############		
//##########################################################################################
//##########################################################################################




class Material
{
	public:
		
		/// Create a default material (0.0f reflectance, 1.0f index of refraction, 16.0f phong exponent)
		inline Material()
			:	reflectance( 0.0f ),
				refraction( 1.0f ),
				phongExponent( 16.0f )
		{
		}
		
		
		/// Create a material with the given reflectance, refraction, and phong exponent.
		inline Material( float newReflectance, float newRefraction, float newPhongExponent )
			:	reflectance( newReflectance ),
				refraction( newRefraction ),
				phongExponent( newPhongExponent )
		{
		}
		
		
		/// 1.0 = fully reflective, 0.0 = fully diffuse
		float reflectance;
		
		
		/// 1.0 = index of vacuum (air)
		float refraction;
		
		
		/// The phong exponent of the material.
		float phongExponent;
		
		
};




//##########################################################################################
//##########################################################################################
//############		
//############		Normal Class
//############		
//##########################################################################################
//##########################################################################################




/// A class representing a normal in 3D space.
class Normal
{
	public:
		
		/// Construct a default normal pointing in the positive X direction.
		inline Normal()
			:	x( 0.0f ),
				y( 0.0f ),
				z( 0.0f )
		{
		}
		
		
		/// Construct a default normal pointing in the given direction.
		inline Normal( float newX, float newY, float newZ )
			:	x( newX ),
				y( newY ),
				z( newZ )
		{
		}
		
		
		/// 3D direction of the normal.
		float x, y, z;
		
		
};




//##########################################################################################
//##########################################################################################
//############		
//############		Vertex Class
//############		
//##########################################################################################
//##########################################################################################




/// A class representing a vertex in 3D space.
class Vertex
{
	public:
		
		/// Construct a default vertex at the origin with texture coordinates (0.0f,0.0f), default color.
		inline Vertex()
			:	x( 0.0f ),
				y( 0.0f ),
				z( 0.0f ),
				u( 0.0f ),
				v( 0.0f )
		{
		}
		
		
		/// Construct vertex at the given position with texture coordinates (0.0f,0.0f), default color.
		inline Vertex( float newX, float newY, float newZ )
			:	x( newX ),
				y( newY ),
				z( newZ ),
				u( 0.0f ),
				v( 0.0f )
		{
		}
		
		
		/// Construct a vertex with the given position, color, with texture coordinates (0.0f,0.0f).
		inline Vertex( float newX, float newY, float newZ, const Color& newColor )
			:	x( newX ),
				y( newY ),
				z( newZ ),
				color( newColor ),
				u( 0.0f ),
				v( 0.0f )
		{
		}
		
		
		/// Construct a vertex with the given position, color, and texture coordinates.
		inline Vertex( float newX, float newY, float newZ, const Color& newColor, float newU, float newV )
			:	x( newX ),
				y( newY ),
				z( newZ ),
				color( newColor ),
				u( newU ),
				v( newV )
		{
		}
		
		
		/// Construct a vertex with the given position, color, and texture coordinates.
		inline Vertex( float newX, float newY, float newZ, const Color& newColor,
						float newU, float newV, const Normal& newNormal )
			:	x( newX ),
				y( newY ),
				z( newZ ),
				color( newColor ),
				u( newU ),
				v( newV ),
				normal( newNormal )
		{
		}
		
		
		/// 3D Position of the vertex.
		float x, y, z;
		
		
		/// Texture coordinates of the vertex.
		float u, v;
		
		
		/// Color of the vertex.
		Color color;
		
		
		/// Normal vector of the vertex.
		Normal normal;
		
		
};




//##########################################################################################
//##########################################################################################
//############		
//############		Triangle Class
//############		
//##########################################################################################
//##########################################################################################




/// A class representing a triangle in 3D space.
class Triangle
{
	public:
		
		/// Create a default triangle.
		inline Triangle()
		{
		}
		
		
		/// Create a triangle with the specified vertices and a default material.
		inline Triangle( const Vertex& v1, const Vertex& v2, const Vertex& v3 )
		{
			v[0] = v1;
			v[1] = v2;
			v[2] = v3;
		}
		
		
		/// Create a triangle with the specified vertices and material.
		inline Triangle( const Vertex& v1, const Vertex& v2, const Vertex& v3,
						const Material& newMaterial )
			:	material( newMaterial )
		{
			v[0] = v1;
			v[1] = v2;
			v[2] = v3;
		}
		
		
		/// The vertices of the triangle.
		Vertex v[3];
		
		
		/// The material of the triangle.
		Material material;
		
		
};




//##########################################################################################
//##########################################################################################
//############		
//############		Mesh Class
//############		
//##########################################################################################
//##########################################################################################




/// A class representing a collection of 3D triangles.
class Mesh
{
	public:
		
		/// Create a default mesh with no triangles.
		inline Mesh()
		{
		}
		
		
		/// Create a mesh with the specified list of triangles.
		inline Mesh( const std::vector<Triangle>& newTriangles )
			:	triangles( newTriangles )
		{
		}
		
		
		/// Create a mesh with the specified list of triangles.
		inline Mesh( const std::vector<Triangle>& newTriangles, const Transform& newTransform,
					const std::string& newName )
			:	triangles( newTriangles ),
				transform( newTransform ),
				name( newName )
		{
		}
		
		
		/// The triangles of the mesh.
		std::vector<Triangle> triangles;
		
		
		
		/// The transformation to be applied to the mesh.
		Transform transform;
		
		
		
		/// The name of the mesh, used for instancing.
		std::string name;
		
		
};




//##########################################################################################
//##########################################################################################
//############		
//############		Instance
//############		
//##########################################################################################
//##########################################################################################




/// A class representing an instance of an object.
class Instance
{
	public:
		
		/// Create a default instance which does not point to any object.
		inline Instance()
		{
		}
		
		
		/// Create an instance with the specified source name.
		inline Instance( const std::string& newSourceName )
			:	sourceName( newSourceName )
		{
		}
		
		
		/// Create an instance with the specified source name and transformation.
		inline Instance( const std::string& newSourceName, const Transform& newTransform )
			:	sourceName( newSourceName ),
				transform( newTransform )
		{
		}
		
		
		/// The name of the source object.
		std::string sourceName;
		
		
		/// The 3D transformation to apply to the source object.
		Transform transform;
		
		
};




//##########################################################################################
//##########################################################################################
//############		
//############		Light Class
//############		
//##########################################################################################
//##########################################################################################




/// A class representing a 3D point light source.
class Light
{
	public:
		
		/// Create a light source at the origin with white color.
		inline Light()
			:	color( 1.0f, 1.0f, 1.0f ),
				x( 0.0f ),
				y( 0.0f ),
				z( 0.0f ),
				ambient( 0.0f )
		{
		}
		
		
		/// Create a light source at the given position with white color.
		inline Light( float newX, float newY, float newZ )
			:	color( 1.0f, 1.0f, 1.0f ),
				x( newX ),
				y( newY ),
				z( newZ ),
				ambient( 0.0f )
		{
		}
		
		
		/// Create a light source with the given position and color.
		inline Light( float newX, float newY, float newZ, const Color& newColor, float newAmbient )
			:	color( newColor ),
				x( newX ),
				y( newY ),
				z( newZ ),
				ambient( newAmbient )
		{
		}
		
		
		
		
		/// The color of the light.
		Color color;
		
		
		
		
		/// The 3D position of the light.
		float x, y, z;
		
		
		
		/// The ambient contribution of the light's color (from 0.0 to 1.0)
		float ambient;
		
		
};

class DirectionalLight : public Light
{
public:

	inline DirectionalLight()
	{
	}

	inline DirectionalLight( float newX, float newY, float newZ, 
		float newLookAtX, float newLookAtY, float newLookAtZ )
		: Light( newX, newY, newZ ),
		lookAtX( newLookAtX ),
		lookAtY( newLookAtY ),
		lookAtZ( newLookAtZ )
	{

	}

	inline DirectionalLight( float newX, float newY, float newZ, 
		float newLookAtX, float newLookAtY, float newLookAtZ,
		const Color& newColor, float newAmbient)
		: Light( newX, newY, newZ, newColor, newAmbient ),
		lookAtX( newLookAtX ),
		lookAtY( newLookAtY ),
		lookAtZ( newLookAtZ )
	{

	}

	float lookAtX, lookAtY, lookAtZ;
};

class SpotLight : public DirectionalLight
{
public:

	inline SpotLight()
	{
	}

	inline SpotLight( float newX, float newY, float newZ, 
		float newLookAtX, float newLookAtY, float newLookAtZ,
		float newCutOff, float newExponent )
		: DirectionalLight( newX, newY, newZ, newLookAtX, newLookAtY, newLookAtZ ),
		cutOff( newCutOff ),
		exponent( newExponent )
	{

	}

	inline SpotLight( float newX, float newY, float newZ, 
		float newLookAtX, float newLookAtY, float newLookAtZ,
		const Color& newColor, float newAmbient,
		float newCutOff, float newExponent )
		: DirectionalLight( newX, newY, newZ, newLookAtX, newLookAtY, newLookAtZ, newColor, newAmbient ),
		cutOff( newCutOff ),
		exponent( newExponent )
	{

	}

	float cutOff;
	float exponent;
};


//##########################################################################################
//##########################################################################################
//############		
//############		Sphere Class
//############		
//##########################################################################################
//##########################################################################################

class KeyFrame
{
public:
	KeyFrame(float _fTime, float _x, float _y, float _z)
		: fTime(_fTime)
		, x(_x)
		, y(_y)
		, z(_z)
	{
	}

	float fTime;
	float x, y, z;

};

/// A class representing a sphere.
class Sphere
{
	public:
		
		/// Create a default white sphere centered at the origin with a radius of 1.
		inline Sphere()
			:	color( 1.0f, 1.0f, 1.0f ),
				radius( 1.0f ),
				x( 0.0f ),
				y( 0.0f ),
				z( 0.0f )
		{
		}
		
		
		/// Create a white sphere with the specified radius and position.
		inline Sphere( float newRadius, float newX, float newY, float newZ )
			:	color( 1.0f, 1.0f, 1.0f ),
				radius( newRadius ),
				x( newX ),
				y( newY ),
				z( newZ )
		{
		}
		
		
		/// Create a sphere with the specified radius, position, and color.
		inline Sphere( float newRadius, float newX, float newY, float newZ,
						const Color& newColor, const Material& newMaterial,
						std::vector<KeyFrame> newKeyFrames)
			:	color( newColor ),
				material( newMaterial ),
				radius( newRadius ),
				x( newX ),
				y( newY ),
				z( newZ ),
				keyFrames( newKeyFrames )
		{
		}
		
		
		
		/// The radius of the sphere.
		float radius;
		
		
		/// The 3D position of the sphere.
		float x, y, z;
		
		
		/// The color of the sphere.
		Color color;
		
		
		/// The material properies of the sphere.
		Material material;
		
		std::vector<KeyFrame> keyFrames;
};

class CSGUnion
{
public:
	inline CSGUnion(const Sphere& newLeft, const Sphere& newRight)
		: left(newLeft)
		, right(newRight)
	{
	}

	Sphere left;
	Sphere right;
};

class CSGIntersection
{
public:
	inline CSGIntersection(const Sphere& newLeft, const Sphere& newRight)
		: left(newLeft)
		, right(newRight)
	{
	}

	Sphere left;
	Sphere right;
};

class CSGDifference
{
public:
	inline CSGDifference(const Sphere& newLeft, const Sphere& newRight)
		: left(newLeft)
		, right(newRight)
	{
	}

	Sphere left;
	Sphere right;
};

//##########################################################################################
//##########################################################################################
//############		
//############		Camera Class
//############		
//##########################################################################################
//##########################################################################################




/// A class representing a camera viewing a scene.
class Camera
{
	public:
		
		/// Create a default camera centered at the origin looking in the positive z direction.
		inline Camera()
			:	fov( 90.0f ),
				x( 0.0f ), y( 0.0f ), z( 0.0f ),
				lookAtX( 0.0f ), lookAtY( 0.0f ), lookAtZ( 1.0f ),
				upX( 0.0f ), upY( 1.0f ), upZ( 0.0f )
		{
		}
		
		
		/// Create a camera with the specified attributes.
		inline Camera( float newFOV, float newX, float newY, float newZ,
						float newLookAtX, float newLookAtY, float newLookAtZ,
						float newUpX, float newUpY, float newUpZ )
			:	fov( newFOV ),
				x( newX ), y( newY ), z( newZ ),
				lookAtX( newLookAtX ), lookAtY( newLookAtY ), lookAtZ( newLookAtZ ),
				upX( newUpX ), upY( newUpY ), upZ( newUpZ )
		{
		}
		
		
		
		/// The field of view of the camera.
		float fov;
		
		
		/// The 3D position of the camera.
		float x, y, z;
		
		
		/// The point at which the camera is looking from its position.
		float lookAtX, lookAtY, lookAtZ;
		
		
		/// The up vector of the camera, the direction that will be pointed up on the screen.
		float upX, upY, upZ;
		
		
};




//##########################################################################################
//##########################################################################################
//############		
//############		Scene Class
//############		
//##########################################################################################
//##########################################################################################




/// A class representing a simple 3D scene consisting of lights, triangle meshes and spheres.
class Scene
{
	public:
		
		/// Create an empty scene.
		inline Scene()
		{
		}
		
		
		/// Create a scene with the specified meshes, spheres, and lights.
		inline Scene( const Camera& newCamera,
						const std::vector<Mesh>& newMeshes,
						const std::vector<Mesh>& newMeshResources,
						const std::vector<Sphere>& newSpheres,
						const std::vector<Light>& newLights,
						const std::vector<DirectionalLight>& newDirectionalLights,
						const std::vector<SpotLight>& newSpotLights,
						const std::vector<Instance>& newInstances,
						const std::vector<CSGUnion>& newCSGUnions,
						const std::vector<CSGDifference>& newCSGDifferences,
						const std::vector<CSGIntersection>& newCSGIntersections
						)
			:	camera( newCamera ),
				meshes( newMeshes ),
				meshResources( newMeshResources ),
				spheres( newSpheres ),
				lights( newLights ),
				directionalLights( newDirectionalLights ),
				spotLights( newSpotLights ),
				instances( newInstances ),
				csgUnions( newCSGUnions ),
				csgDifferences( newCSGDifferences ),
				csgIntersections( newCSGIntersections )
		{
		}
		
		
		/// The camera of the scene.
		Camera camera;
		
		
		/// The meshes in the scene to be drawn directly.
		std::vector<Mesh> meshes;
		
		
		/// The meshes not in the scene but which may still be used through instancing.
		std::vector<Mesh> meshResources;
		
		
		/// The spheres in the scene.
		std::vector<Sphere> spheres;

		std::vector<CSGUnion> csgUnions;
		std::vector<CSGDifference> csgDifferences;
		std::vector<CSGIntersection> csgIntersections;
		
		
		/// The lights in the scene.
		std::vector<Light> lights;
		std::vector<DirectionalLight> directionalLights;
		std::vector<SpotLight> spotLights;
		
		
		/// The instances in the scene.
		std::vector<Instance> instances;
		
		
};





//##########################################################################################
//##########################################################################################
//############		
//############		Load Methods
//############		
//##########################################################################################
//##########################################################################################




/// Load the scene contained in the XML file with the specified filename.
Scene loadScene( const std::string& fileName );




/// Load the mesh contained in the XML file with the specified filename.
Mesh loadMesh( const std::string& fileName );





//##########################################################################################
//##########################################################################################
//############		
//############		Save Methods
//############		
//##########################################################################################
//##########################################################################################




/// Save the specified scene in an XML file with the specified filename.
void saveScene( const Scene& scene, const std::string& fileName );




/// Save the specified mesh in an XML file with the specified filename.
void saveMesh( const Mesh& mesh, const std::string& fileName );





//##########################################################################################
//##########################################################################################
//############		
//############		Other Methods
//############		
//##########################################################################################
//##########################################################################################




/// Convert all mesh instances into direct meshes within the given scene.
Scene flattenScene( const Scene& scene );



// End namespace "scene"
};



#endif // _INCLUDE_SCENE_LOADER_H_
